/*
 * Matthew Cramer, Jesse Martinez, Marc Mendoza 
 * CSE 325 Lab 4
 * File: QueueManager.c
 * Description: Manager that handles calls to the Queue.  This is called from
 *              the IPC manager to perform the functions to the queue.
 *
 */

#include "QueueManager.h"
#define MAX_PROCESSES 10
#define MAX_Queue_SIZE 10

/*
 * Declare functions
 */
void Enqueue(int, int, int, int*, int*, int*, int*, message_block*);
message_block Dequeue(int*, int*, int*, int*, message_block*);
void iniFree(int*, int*, message_block*);
void iniQueue(int*, int*, int*, int*, int*);

/*
 * Enqueue: Receives information from Testing Interface to put onto Queue
 */
void Enqueue(int source, int destination, int message, 
             int* head, int* tail, int* freeHead, int* freeTail, 
             message_block* Queue)
{   
    //enqueues onto an empty Queue, insert one element
    if(*head==-1 && *tail==-1)
    {
        *head = *freeHead;
		*tail = *freeHead;
		*freeHead = Queue[*freeHead].nextIndex;
        if(*freeHead == -1)
        {
            *freeTail = -1;
        }
        else
        {
            Queue[*freeHead].prevIndex = -1;
        }
		Queue[*head].prevIndex = -1;
		Queue[*tail].nextIndex = -1;
    }
	//enqueues onto a partially filled Queue
	else
	{
		Queue[*tail].nextIndex = *freeHead;
		Queue[*freeHead].prevIndex = *tail;
		*tail = *freeHead;
		*freeHead = Queue[*freeHead].nextIndex;
        //Null FreeList
        if(*freeHead == -1)
        {
            *freeTail = -1;
        }
        else
        {
            Queue[*freeHead].prevIndex = -1;
        }
        Queue[*tail].nextIndex = -1;
	}
    
    /*
     * Insert paramaters into struct at open slot
     */
    Queue[*tail].source = source;
    Queue[*tail].destination = destination;
    Queue[*tail].message = message;
}

/*
 * DeQueue: returns the message block at head of Queue and Deletes it, -1 if no elements
 */
message_block Dequeue(int* head, int* tail, int* freeHead, int* freeTail, message_block* Queue)
{
	int temp = *head;
	if( *head == *tail)//sets head and tail to null if there is only one block
	{
		*tail = -1;
		*head = -1;
	}
	else
    {
		*head = Queue[*head].nextIndex;
        Queue[*head].prevIndex = -1;
    }
    
    //if there is nothing on free list
	if(*freeHead == -1 && *freeTail == -1)
	{
		*freeHead = temp;
		*freeTail = temp;
		Queue[*freeHead].nextIndex = -1;
		Queue[*freeHead].prevIndex = -1;
	}
	else
	{
        //move free slot to free list
		Queue[*freeTail].nextIndex = temp;
		Queue[temp].prevIndex = *freeTail;
		Queue[temp].nextIndex = -1;
		*freeTail = temp;
	}
	return Queue[temp];
}

//Reset the Free List for the given Queue
void iniFree(int* head, int* tail, message_block* Queue)
{
	int i;
	for(i = 0; i < *tail; i++)
	{
		Queue[i].prevIndex = i-1;
		Queue[i].nextIndex = i+1;
	}
	Queue[*tail].prevIndex = i-1;
	Queue[*tail].nextIndex = -1;
}

//Clears all processes in Queue
void iniQueue(int* head, int* tail, int* freeHead, int* freeTail, int* size)
{
    *head=-1;
    *tail=-1;
    *freeHead = 0;
    *freeTail = MAX_PROCESSES-1;
    *size = 0;
}

